﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  SmsMessageSender.cs
//    
// Abstract:
//
//  Implementation of IMessageSender for delivering notifications via SMS. Note that
//  this class uses the credentials of the process the Dispatcher is running under for
//  authenticating with the SMTP server.
//  
//--

using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.Mail;
using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders.Dispatcher
{
    /// <summary>
    /// Implementation of IMessageSender for delivering notifications via SMS
    /// </summary>
    [StopPoint(false)]
    public class SmsMessageSender : IMessageSender
	{
        /// <summary>
        /// The maximum number of characters allowed in an SMS message.
        /// </summary>
        public static readonly int SmsCharacterLimit = 160;

        private NotificationItem _notification;
        private string _destination;

        /// <summary>
        /// ctor.
        /// </summary>
        public SmsMessageSender(NotificationItem notification, string destination)
        {
            Debug.Assert(notification != null, "notification != null");
            Debug.Assert(!string.IsNullOrEmpty(destination), "!string.IsNullOrEmpty(destination)");

            _notification = notification;
            _destination = destination;
        }

        #region IMessageSender Members

        /// <summary>
        /// Event handler to be called when a notification has been delivered.
        /// </summary>
        public event EventHandler<DeliveryCompletedEventArgs> DeliverMessageCompleted;

        /// <summary>
        /// Asynchronously deliver a notification via SMS via e-mail. 
        /// </summary>
        public void DeliverMessageAsync()
        {
            try
            {
                MailMessage emailMessage = new MailMessage(Environment.UserName + "@" + Helpers.GetSetting<string>("BotDomain",null),
                    _destination, "", GetSmsBodyForNotification(_notification));
                SmtpClient mailClient = new SmtpClient(Helpers.GetSetting<string>("SmtpServer",""), Helpers.GetSetting<int>("SmtpPort",-1));

                mailClient.Credentials = (ICredentialsByHost)CredentialCache.DefaultNetworkCredentials;
                mailClient.Send(emailMessage);
            }
            catch (FormatException e)
            {
                Logger.WriteTraceError(_notification.Context, e, "Error sending SMS via e-mail. Destination e-mail address was not correctly formatted.");
                OnDeliverMessageCompleted(new NotificationResult(_notification,
                    NotificationResultStatus.Error, NotificationChannelType.SMS));

                return;
            }
            catch (ArgumentOutOfRangeException e)
            {
                OperationalLogs.LogSmsNotificationError(_destination, e);
                OnDeliverMessageCompleted(new NotificationResult(_notification,
                    NotificationResultStatus.Error, NotificationChannelType.SMS));

                return;
            }
            catch (SmtpException e)
            {
                OperationalLogs.LogSmsNotificationError(_destination, e);
                OnDeliverMessageCompleted(new NotificationResult(_notification,
                    NotificationResultStatus.Error, NotificationChannelType.SMS));

                return;
            }
            catch (InvalidOperationException e)
            {
                OperationalLogs.LogSmsNotificationError(_destination, e);
                OnDeliverMessageCompleted(new NotificationResult(_notification,
                    NotificationResultStatus.Error, NotificationChannelType.SMS));

                return;
            }

            OnDeliverMessageCompleted(new NotificationResult(_notification,
                NotificationResultStatus.Success, NotificationChannelType.SMS));
        }

        /// <summary>
        /// Raises the message completed event.
        /// </summary>
        private void OnDeliverMessageCompleted(NotificationResult result)
        {
            EventHandler<DeliveryCompletedEventArgs> callback = DeliverMessageCompleted;

            if (callback != null)
            {
                callback(this, new DeliveryCompletedEventArgs(result));
            }
        }

        /// <summary>
        /// Generates the body of an SMS notification.
        /// </summary>
        private static string GetSmsBodyForNotification(NotificationItem notification)
        {
            VoicemailNotificationItem voicemailNotification = notification as VoicemailNotificationItem;
            CalendarNotificationItem calendarNotification = notification as CalendarNotificationItem;
            MeetingNotificationItem meetingNotification = notification as MeetingNotificationItem;

            if (voicemailNotification != null)
            {
                string message = string.Format(CultureInfo.CurrentUICulture, IMResources.Sms_Voicemail,
                    voicemailNotification.VoicemailCallerId);

                if (message.Length <= SmsCharacterLimit)
                {
                    return message;
                }
                else
                {
                    return message.Substring(0, SmsCharacterLimit);
                }
            }
            else if (meetingNotification != null)
            {
                return GetAppointmentOrMeetingSmsMessage(calendarNotification, IMResources.Sms_Meeting);
            }
            else if (calendarNotification != null)
            {
                return GetAppointmentOrMeetingSmsMessage(calendarNotification, IMResources.Sms_Appointment);
            }

            Debug.Fail("Unknown notification type encountered when trying to send SMS notification");
            return null;
        }

        /// <summary>
        /// Generates the body of a meeting or appointment notification.
        /// </summary>
        private static string GetAppointmentOrMeetingSmsMessage(CalendarNotificationItem calendarNotification, string formatString)
        {
            Debug.Assert(calendarNotification != null, "calendarNotification != null");

            string leadTime = DispatcherHelpers.GetLeadTime(CultureInfo.CurrentUICulture, TimeZoneInfo.Local, calendarNotification.StartTime, new SmsTimeSpanFormatter());
            int wildCardCharacterLength = 9;
            int allowedSubjectAndLocationLength = SmsCharacterLimit - formatString.Length + wildCardCharacterLength - leadTime.Length;

            int messageLength = string.IsNullOrEmpty(calendarNotification.Message) ? 0 : calendarNotification.Message.Length;
            int locationLength = string.IsNullOrEmpty(calendarNotification.Location) ? 0 : calendarNotification.Location.Length;

            if (messageLength + locationLength <= allowedSubjectAndLocationLength)
            {
                return string.Format(CultureInfo.CurrentUICulture, formatString, leadTime, calendarNotification.Message, calendarNotification.Location);
            }
            else
            {
                int lengthPerField = allowedSubjectAndLocationLength / 2;

                return string.Format(CultureInfo.CurrentUICulture, formatString, leadTime,
                    messageLength > lengthPerField ? calendarNotification.Message.Substring(0, lengthPerField) : calendarNotification.Message,
                    locationLength > lengthPerField ? calendarNotification.Location.Substring(0, lengthPerField) : calendarNotification.Location);
            }
        }

        #endregion
    }
}
